#include "Analysis.h"

using namespace FitMethods;
using namespace EfficCalcMethods;
using namespace Paint;

void Analysis::runAnalysis() {
    /** Loading all histograms based on the configuration passed through config. */
    IOmanager & IOman = IOmanager::instance();
    IOman.openInput(config.getDataFileName());
    cout << "probeData" << endl;
    probeData = getHistogram(config.getProbeDataTempl());
    cout << "selectedData" << endl;
    selectedData = getHistogram(config.getSelectedDataTempl());
    IOman.closeInput();
    IOman.openInput(config.getBgdTemplFileName());
    cout << "probeBgdTempl" << endl;
    loadHistograms(config.getProbeBgdTempl(), probeBgdTempl);
    cout << "selectedBgdTempl" << endl;
    loadHistograms(config.getSelectedBgdTempl(), selectedBgdTempl);
    IOman.closeInput();
    IOman.openInput(config.getSigTemplFileName());
    cout << "probeSigTempl" << endl;
    loadHistograms(config.getProbeSigTempl(), probeSigTempl);
    cout << "selectedSigTempl" << endl;
    loadHistograms(config.getSelectedSigTempl(), selectedSigTempl);
    IOman.closeInput();
    
    AbsFitMethod * fitMethod;
    if(config.getIsMethodRaw()) fitMethod = new MethodRaw(&gc);
    else if(config.getIsMethodSubtraction()) fitMethod = new MethodSubtraction(&gc);
    else if(config.getIsMethodEXLL() || config.getIsMethodCHI2()) fitMethod = new MethodMinimization(&gc);
    else throw AnalysisException("The fit method passed in AnalysisConfig is not any currently implemented fit method.");
    gc.add(fitMethod);
    FitResult probeFitResult = fitMethod->runAnalysisCase(config,*new FitInput(probeData,probeSigTempl,probeBgdTempl,&gc));
    FitResult selectedFitResult = fitMethod->runAnalysisCase(config,*new FitInput(selectedData,selectedSigTempl,selectedBgdTempl,&gc));
    Painting * painting = new Painting(&gc);
    painting->draw(config,*new FitInput(probeData,probeSigTempl,probeBgdTempl,&gc),probeFitResult);
    painting->draw(config,*new FitInput(selectedData,selectedSigTempl,selectedBgdTempl,&gc),selectedFitResult);
    cout << "Probe result:" << endl;
    probeFitResult.print();
    cout << "Selected result:" << endl;
    selectedFitResult.print();

    AbsEfficCalcMethod * efficCalcMethod;
    if(config.getIsMethodBinomialSubtraction()) efficCalcMethod = new MethodBinomialSubtraction(&gc);
    else if(config.getIsMethodFromFit()) efficCalcMethod = new MethodFromFit(&gc);
    else if(config.getIsMethodDirect()) efficCalcMethod = new MethodDirect(&gc);
    else if(config.getIsMethodIndirect()) efficCalcMethod = new MethodIndirect(&gc);
    else throw AnalysisException("The efficiency calculation method passed in AnalysisConfig is not implemented");
    gc.add(efficCalcMethod);
    RooRealVar efficiency = efficCalcMethod->getEfficiency(probeFitResult.getSignal(),probeFitResult.getBackground(),
            selectedFitResult.getSignal(),selectedFitResult.getBackground());
    efficiency.Print();
    AnalysisResult result;
    config.dump(result);
    probeFitResult.dump(result.probe);
    selectedFitResult.dump(result.selected);
    result.setEfficiency(efficiency.getVal());
    result.setEfficiencyError(efficiency.getError());
    result.save();
}

/** This method loads the histograms stipulated by the objects in the first parameter and inserts pointers to them in the vector passed by the second parameter. All histograms are automatically added to this->gc. */
void Analysis::loadHistograms(const vector <HistogramConfig> & templateConfigVector, vector <TH1D *> & resultVector) {
    /** Loading background templates */
    for(vector <HistogramConfig>::const_iterator it = templateConfigVector.begin();it!=templateConfigVector.end();it++) {
        resultVector.push_back(getHistogram(*it));
    }
}

/** This method returns the histogram stipulated by the HistogramConfig object passed to it. The histogram is added to this->gc. */
TH1D * Analysis::getHistogram(HistogramConfig h) {
     TH1::AddDirectory(kFALSE);
     IOmanager & IOman = IOmanager::instance();
     cout << "Histogram: " << h.number << " " << h.ETAMin << " " << h.ETAMax << " " << h.binMin << " " << h.binMax << endl;
     TH1D * hist = IOman.getProjectionZ(h.number,h.ETAMin,h.ETAMax,h.binMin,h.binMax);
     //hist->Print("all");
     gc.add(hist);
     return hist;
}
